package ro.gii.auctionHouse.security;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.ldap.userdetails.LdapUserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import ro.gii.auctionHouse.model.User;
import ro.gii.auctionHouse.model.UserRole;
import ro.gii.auctionHouse.service.UserService;

/**
 * 
 * <p>
 * If the user trying to login has an empty/null password in the database then
 * the authentication is made by LDAP.
 * <p>
 * If the user has a non-empty password specified inside the database then the
 * authentication is made internally.
 * <p>
 * In both cases, the roles of the user are initialized as they are present in
 * the database. As a consequence, a user that doesn't exist in the database,
 * cannot connect to the application, not even through LDAP).
 * 
 */
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

	/**
	 * The user service.
	 */
	@Autowired
	private UserService userService;

	/**
	 * The ldap authentication provider.
	 */
	

	@Override
	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {

		String username = authentication.getName();
		String password = (String) authentication.getCredentials();

		if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
			throw new BadCredentialsException("Empty credentials.");
		}

		User user = userService.getUserByLogin(username);

		if (user == null) {
			// All users have to exist in the database, even if the password is
			// empty and the authentication should be made by LDAP.
			// We need all users in the database to be able to initialize their
			// roles inside the application.
			throw new BadCredentialsException("Incorrect credentials.");
		}

		if (!user.isActive()) {
			throw new BadCredentialsException("Provided username is not active");
		}

		if (StringUtils.isEmpty(user.getPassword())) {
			throw new BadCredentialsException("Incorrect credentials.");
		} else {
			// internal authentication
			boolean loginOk = userService.loginUser(username, password);

			if (!loginOk) {
				throw new BadCredentialsException("Incorrect credentials.");
			}
		}

		Collection<GrantedAuthority> authorities = new ArrayList<>();

		for (UserRole userRole : user.getUserRoles()) {
			authorities.add(new SimpleGrantedAuthority("ROLE_"
					+ userRole.getRole().getCode()));
		}

		// don't store the password on the security context
		Authentication geiAuthentication = new UsernamePasswordAuthenticationToken(
				username, null, authorities);

		return geiAuthentication;
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return UsernamePasswordAuthenticationToken.class
				.isAssignableFrom(authentication);
	}

}